iT邦幫忙

2023 iThome 鐵人賽

DAY 22
0
Modern Web

自己開發一個~?系列 第 22

Springboot~練習刪除~

  • 分享至 

  • xImage
  •  

今天參加完JCConf 2023又獲得能量滿滿
/images/emoticon/emoticon07.gif

來寫刪除的動作,加入刪除對話盒

修改前端程式碼

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
   
    <script th:src="@{/js/vue.min.js}"></script>
    <script th:src="@{/js/jquery-3.6.1.min.js}"></script>
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.2/dist/css/bootstrap.min.css" integrity="sha384-xOolHFLEh07PJGoPkLv1IbcEPTNtaed2xpHsD9ESMhqIYd0nLMwNLD69Npy4HI+N" crossorigin="anonymous">
    <link rel="stylesheet" href="//code.jquery.com/ui/1.13.2/themes/base/jquery-ui.css">
    <link rel="stylesheet" href="/resources/demos/style.css">
    <script src="https://code.jquery.com/ui/1.13.2/jquery-ui.js"></script>
    <title>客戶清單與維護作業</title>
    <!--設定樣式-->
    <!--<style>
       body{
        background-color: lavenderblush;border: 2px;
       }
       #app{
        background-color: aqua;
        font-size: 16px;
        color:darkblue;
       }
       .btnEdit{
        background-color: black;
        color:white;
       }
       #data tr:nth-child(odd){
        background-color: coral;
        color:black;
       }
       #data tr:nth-child(even){
        background-color: rgb(220, 210, 156);
        color:black;
       }
    </style>-->
    <style>
        thead{
            background-color: lemonchiffon;
            color:black;
            font-size: 18px;
            font-style: unset;
        }
        .rowHight{
            background-color: bisque;
            color:rgb(7, 1, 18);
        }
    </style>
</head>
<body id="body">
    <script th:inline="javascript">
        //定義變數 陣列內容???後端使用thymeleaf嵌入進來的[{,,,},{}]
        var list=/*[[${data}]]*/ [];
        //事件程序綁在標籤body標籤物件 onload事件上
        //window.alert(document.getElementById('body'));
        //標籤物件操作模組 DOM(Document Object Model)
        document.getElementById('body').onload=function(){
            //alert('我來了');
            //參照標籤物件label
            document.getElementById('counter').innerText='客戶記錄數:'+list.length;
        }
    </script>
    <fieldset id="app">
        <legend>客戶清單</legend>
        <div>
            <label id="counter"></label>
        </div>
        <table class="table table-dark table-hover">
            <thead>
                <tr>
                    <td>操作</td>
                    <td>客戶編號</td>
                    <td>公司行號</td>
                    <td>聯絡地址</td>
                    <td>連絡電話</td>
                    <td>EMAIL</td>
                    <td>國家別</td>
                </tr>
            </thead>
            <!--借助JS MVVM Framework -->
            <tbody id="data">
                <tr v-for="(item,index) in customerList">
                    <td>
                        <!--按鈕事件程序 給Vue進行綁定function來執行-->
                        <!--如何知道這一個按鈕被觸發相對列???-->
                        <button class="btn btn-primary" v-on:click="editHandler" v-bind:accessKey="index">編輯</button>
                        <button class="btn btn-danger" v-on:click="deleteHandler" v-bind:accessKey="index">刪除</button>
                    </td>
                    <td>{{item.customerid}}</td>
                    <td>{{item.companyname}}</td>
                    <td>{{item.address}}</td>
                    <td>{{item.phone}}</td>
                    <td>{{item.email}}</td>
                    <td>{{item.country}}</td>
                </tr>
            </tbody>
        </table>

        <!--編輯相對客戶資料對話盒-->
        <fieldset id="editDialog" style="display: none;">
            <legend>客戶資料編輯</legend>
            <table>
                <tr>
                    <td>客戶編號</td>
                    <td><input type="text" v-model:value="curCustomers.customerid" readonly></td>
                </tr>
                <tr>
                    <td>公司行號</td>
                    <td><input type="text" v-model.lazy:value="curCustomers.companyname"></td>
                </tr>
                <tr>
                    <td>聯絡地址</td>
                    <td><input type="text" v-model:value="curCustomers.address"></td>
                </tr>
                <tr>
                    <td>連絡電話</td>
                    <td><input type="text" v-model:value="curCustomers.phone"></td>
                </tr>
                <tr>
                    <td>EMAIL</td>
                    <td><input type="text" v-model:value="curCustomers.email"></td>
                </tr>
                <tr>
                    <td>國家別</td>
                    <td><input type="text" v-model:value="curCustomers.country"></td>
                </tr>
            </table>
            <h3>{{message}}</h3>
        </fieldset>
        <!--刪除對話盒-->
        <fieldset id="deleteDialog" style="display: none;">
           
            <h2>客戶編號:{{curCustomers.customerid}}</h2>
            <h2>公司行號:{{curCustomers.companyname}}</h2>
        </fieldset>
       
    </fieldset>

    <script>
        //使用jquery selector挑選文件物件 .ready() 綁定聆聽已經完整下載之後引發事件
        $(document).ready(
            //事件程序
            function(){
                //alert('hi jquery');
            }
        );
        //建構Vue物件
        var app=new Vue(
            {
                //資料模組
                data:{
                    customerList:[], //空陣列
                    rowIndex:-1, //挑選的相對順序
                    curCustomers:{}, //相對列 相對客戶物件
                    message:'' //異動資料回乎訊息
                },
                //設定Vue支援共用程序 或者事件程序綁定來源
                methods:{
                    //聆聽編輯按鈕被觸發
                    editHandler:function(e){
                       //reset
                       this.message='';
                        //取出按鈕的accessKey屬性 在一開始渲染畫面就設定相對資料列順序
                        let index=e.target.accessKey;
                        //透過順序對應Vue customerList取出相對的客戶資料
                        this.curCustomers=this.customerList[index];
                        //console.log(currentCustomers);
                        //HighLight資料的相對列
                        //先行移除原先挑選列的class
                        $('tbody tr').eq(app.rowIndex).removeClass('rowHight');
                        //使用選擇器挑選標籤 子標籤 eq(順序) 呼叫addClass Method動態加入class=""
                        $('tbody tr').eq(index).addClass('rowHight'); //選取tbody內所有的tr(所有列物件)
                        //將相對列進行管理
                        app.rowIndex=index;
                        //TODO 啟動對話盒 編輯相對列物件
                        $('#editDialog').dialog(
                            //初始化物件設定(抬頭/寬度/按鈕(s)/強佔式對話盒)
                            {
                                title:'客戶資料編輯',
                                modal:true,
                                width:360, //按鈕設計
                                buttons:[
                                    {
                                        text:'更新',
                                        class:'btn btn-primary',
                                        click:function(){
                                            //TODO 進行非同步處理更新後端資料
                                            //1建構一個XMLHttpRequest物件
                                            let xhr=new XMLHttpRequest();
                                            
                                            //2.設定開啟遠端服務位址
                                            xhr.open('PUT','../api/customers/update/rawdata');
                                            //沒有設定Request Header Content-Type:application/json
                                            xhr.setRequestHeader("Content-Type","application/json")
                                            //進行非同步 採用輪詢 必須設定callback程序進行處理
                                            xhr.onreadystatechange=function(e){
                                                if(xhr.readyState==4 && xhr.status==200){
                                                    //取出資料(JSON String)
                                                    let resultString=xhr.responseText;
                                                    //String反序列化成物件
                                                    let result=JSON.parse(resultString);
                                                    //使用app指定Vue物件成員
                                                    app.message=result.msg;//將訊息指派給Vue資料庫模組
                                                    console.log(resultString);
                                                }
                                            }
                                            //JSON內容
                                            let jsonString=JSON.stringify(app.curCustomers);
                                            console.log(jsonString);
                                            //正式送出去
                                            xhr.send(jsonString);
                                        }
                                    },
                                    {
                                        text:'關閉',
                                        class:'btn btn-danger',
                                        click:function(){
                                            //關閉對話盒
                                            $('#editDialog').dialog('close');
                                        }
                                    }
                                ]
                            }
                        );
                        //進行編輯畫面的渲染
                    },
                    //按鈕刪除事件
                    deleteHandler:function(e){
                    
                        //1.取得相對被選取的記錄
                        let indexer=e.target.accessKey;
                        //取出相對客戶資料物件 指派給Vue資料模組
                        this.curCustomers=this.customerList[indexer];
                        console.log(this.curCustomers);
                           //Hight UI要設定
                        //先行移除原先挑選列的class
                        $('tbody tr').eq(app.rowIndex).removeClass('rowHight');
                        //使用選擇器挑選標籤 子標籤 eq(順序) 呼叫addClass Method動態加入class=""
                        $('tbody tr').eq(indexer).addClass('rowHight'); //選取tbody內所有的tr(所有列物件)
                        //將相對列進行管理
                        app.rowIndex=indexer;
                        //2.啟動對話盒 進行刪除作業
                        //使用jquery selector 挑選區塊
                        $('#deleteDialog').dialog(
                            //setting 設定JS物件
                            {
                                title:'客戶資料刪除',
                                //強佔式對話盒
                                modal:true,
                                width:400,
                                height:300,
                                //設定按鈕
                                buttons:[
                                    {
                                        text:'刪除',
                                        click:function(){

                                        },
                                        class:'btn btn-danger'
                                    },
                                    {
                                        text:'關閉',
                                        click:function(){
                                            //挑選對話區塊
                                            $('#deleteDialog').dialog('close');    
                                        },
                                        class:'btn btn-primary'
                                    }
                                ]
                            }
                        );
                    }

                }
                ,
                //當Vue完成掛載之後 引發事件..將JS變數list內容指派給Vue物件資料模組customerList
                mounted:function(){
                    //將list變數內容指派給Vue資料物件模組customerList
                    this.customerList=list;
                    console.log(this.customerList);
                }
            }
        );
        //掛載到特定id去
        app.$mount('#app');
    </script>
</body>
</html>

這是一個 HTML 頁面,與前一個版本相比,增加了刪除客戶的功能。以下是主要的改變:

  1. 新增了一個 "刪除" 按鈕,並為每個按鈕綁定了一個 deleteHandler 函數。

  2. 新增了一個對話框 (#deleteDialog),用於顯示要刪除的客戶資訊。

  3. 當 "刪除" 按鈕被點擊時,它會觸發 deleteHandler 函數。這個函數會取得所選客戶的資訊,將其顯示在刪除對話框中。

  4. 在刪除對話框中,有一個 "刪除" 按鈕,當點擊時,可以執行刪除客戶的操作。你需要在 click 函數中實現實際的刪除操作。

這個版本的頁面已經具備了編輯和刪除客戶的功能,並使用 Vue.js 和 jQuery 進行事件處理和對話框的顯示。如果要實現刪除操作,你需要在 "刪除" 按鈕的 click 函數中添加相應的後端請求。

測試http://localhost:8080/customers/allcustomers

Repository Design Pattern

https://medium.com/@pererikbergman/repository-design-pattern-e28c0f3e4a30

增加一個介面

練習刪除~

程式碼要拋例外throws DataAccessException避免有些錯誤

package com.tzu.domain;

import java.util.List;

import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.JdbcTemplate;

//進行泛型設定Generic
public interface DBRepository<T,K> {
	//屬性setXxxx()注入規範 注入 Jdbctemplate
	public void setJdbcTemplate(JdbcTemplate template);
	//CRUD方法
	public boolean insert(T entity) throws  DataAccessException;
	//查詢 單筆查詢 配合Key
	public T selectForObject(K key) throws  DataAccessException;
	//多筆查詢
	public List<T> selectForList(K key) throws  DataAccessException;
	//修改
	public boolean update(T entity) throws  DataAccessException;
	public boolean delete(K key) throws  DataAccessException;

}

這段代碼是一個泛型介面(DBRepository),用於定義一個通用的資料庫存取庫(Repository)。以下是這個介面中的方法和說明:

  1. setJdbcTemplate(JdbcTemplate template): 這是一個方法,用於設定 JdbcTemplate 實例。通常,你會使用 Spring 框架注入 JdbcTemplate 實例,以便在這個 Repository 中執行 SQL 操作。

  2. insert(T entity): 這個方法用於將一個實體(entity)插入到資料庫中。返回 true 表示插入成功,false 表示插入失敗。

  3. selectForObject(K key): 這個方法用於從資料庫中查詢單筆資料,通常是根據某個唯一的關鍵鍵(key)。它返回一個對象(T),代表查詢結果。

  4. selectForList(K key): 這個方法用於從資料庫中查詢多筆資料,通常是根據某個條件(key)。它返回一個包含多個對象的列表,即 List<T>

  5. update(T entity): 這個方法用於更新資料庫中的一個實體。返回 true 表示更新成功,false 表示更新失敗。

  6. delete(K key): 這個方法用於刪除資料庫中的一條記錄,通常是根據某個唯一的關鍵鍵。返回 true 表示刪除成功,false 表示刪除失敗。

這個介面是泛型化的,它可以應用於不同的資料實體類型(T)和關鍵鍵類型(K)。你可以實現這個介面,然後在具體的 Repository 類別中實作這些方法,以便執行與特定資料實體相關的 CRUD 操作。這通常用於 Spring 應用中,將資料庫操作封裝在 Repository 類別中,以實現數據存取的解耦。

再新增一個檔案:

參考Autowired api:https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/beans/factory/annotation/Autowired.html

參考Repository api:https://docs.spring.io/spring-data/commons/docs/current/api/org/springframework/data/repository/Repository.html

新增檔名public class CustomersRepo及 implements DBRepository<Customers,String>後選unimplemented

package com.tzu.domain;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
@Repository
public class CustomersRepo implements DBRepository<Customers,String> {
	
	private JdbcTemplate template;

	@Override
	public void setJdbcTemplate(JdbcTemplate template) {
		this.template=template;
	}

	@Override
	public boolean insert(Customers entity) throws DataAccessException {
		// TODO Auto-generated method stub
		return false;
	}

	@Override
	public Customers selectForObject(String key) throws DataAccessException {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public List<Customers> selectForList(String key) throws DataAccessException {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public boolean update(Customers entity) throws DataAccessException {
		// TODO Auto-generated method stub
		return false;
	}

	@Override
	public boolean delete(String key) throws DataAccessException {
		
		this.template.update("delete from customers where customerid=?",key);
		return true;
	}
	
}

這是一個具體的 Repository 類別 CustomersRepo,它實現了 DBRepository 介面,針對 Customers 實體和 String 關鍵鍵類型進行操作。以下是這個類別中的方法和說明:

  1. setJdbcTemplate(JdbcTemplate template): 這個方法由 DBRepository 接口定義,用於設定 JdbcTemplate 實例。在這個實作中,你設定了 template,以便在其他方法中使用它來執行 SQL 操作。

  2. insert(Customers entity): 這個方法用於插入一個 Customers 實體到資料庫中。在這個實作中,方法內容是空的,你可以根據需求實作插入邏輯。

  3. selectForObject(String key): 這個方法用於查詢特定 Customers 實體,通常是根據客戶編號 (key)。在這個實作中,方法內容是空的,你可以根據需求實作查詢邏輯。

  4. selectForList(String key): 這個方法用於查詢多個 Customers 實體,通常是根據某個條件。在這個實作中,方法內容是空的,你可以根據需求實作查詢邏輯。

  5. update(Customers entity): 這個方法用於更新資料庫中的 Customers 實體。在這個實作中,方法內容是空的,你可以根據需求實作更新邏輯。

  6. delete(String key): 這個方法用於刪除資料庫中的 Customers 實體,通常是根據客戶編號 (key)。在這個實作中,它使用 JdbcTemplate 執行 SQL 刪除操作,然後返回 true 表示刪除成功。

這個類別上有 @Repository 標註,這表示它是 Spring 框架中的 Repository 類別,並且通常用於執行資料庫操作。這個 CustomersRepo 類別可以被注入到其他 Spring 組件中,以執行與客戶資料相關的 CRUD 操作。

修改這兩個檔案

package com.tzu2.domain;

import java.util.List;

import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.JdbcTemplate;

//進行泛型設定Generic
public interface DBRepository<T,K> {
	//屬性setXxxx()注入規範 注入 Jdbctemplate
	//public void setJdbcTemplate(JdbcTemplate template);
	//CRUD方法
	public boolean insert(T entity) throws  DataAccessException;
	//查詢 單筆查詢 配合Key
	public T selectForObject(K key) throws  DataAccessException;
	//多筆查詢
	public List<T> selectForList(K key) throws  DataAccessException;
	//修改
	public boolean update(T entity) throws  DataAccessException;
	public boolean delete(K key) throws  DataAccessException;

}

這段代碼定義了一個泛型接口DBRepository,是Spring JDBC數據訪問層的典型編碼風格。

代碼解析:

  1. 接口宣告為泛型,定義了兩個泛型參數T和K。

  2. T代表實體對象類型,如Customer

  3. K代表主鍵類型,如String或Long

  4. 提供了CRUD數據庫操作方法,參數和返回值使用泛型

  5. insert方法用于新增記錄

  6. selectForObject查詢單個對象

  7. selectForList查詢多個對象返回List

  8. update用於更新記錄

  9. delete刪除記錄

這種泛型接口編碼方式提高了代碼的可重用性。

實現類只要傳入具體的對象類型和主鍵類型,就得到了一套可重用的CRUD方法。

package com.tzu2.domain;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;

@Repository
public class CustomersRepo implements DBRepository<Customers,String> {
	
	@Autowired
	private JdbcTemplate template;
	
	public CustomersRepo() {
		System.out.println("Customes Repository建構了...");
	}
	
//	//屬性注入 Property Injection(setXxx getXxxx xxx is Property
//	@Override
//	public void setJdbcTemplate(JdbcTemplate template) {
//		this.template=template;
//		
//	}

	@Override
	public boolean insert(Customers entity) throws DataAccessException {
		// TODO Auto-generated method stub
		return false;
	}

	@Override
	public Customers selectForObject(String key) throws DataAccessException {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public List<Customers> selectForList(String key) throws DataAccessException {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public boolean update(Customers entity) throws DataAccessException {
		// TODO Auto-generated method stub
		return false;
	}

	@Override
	public boolean delete(String key) throws DataAccessException {
		
		this.template.update("delete from customers where customerid=?",key);
		return true;
	}
	
}

這段代碼是使用Spring JDBC实现了一个简单的CustomersRepository,主要功能點解析如下:

  1. @Repository註解聲明這是一个數據庫操作Repository

  2. 實現了DBRepository接口,定義了通用的CRUD數據庫操作方法

  3. @Autowired自動注入JdbcTemplate bean來進行數據庫操作

  4. delete方法實現了根據客戶id刪除客戶記錄的功能

  5. 其他方法如insert、update等還沒有具體實現,留作TODO

  6. JdbcTemplate可以直接執行SQL語句進行數據庫CRUD

  7. Repository + JdbcTemplate是Spring中數據訪問層的典型實踐

總結為:

  • 定義Repository接口來統一數據訪問接口
  • 用JdbcTemplate實現具體數據庫操作
  • Repository和JdbcTemplate結合實現數據層的職責
  • 依據接口編程,提高了擴展性與測試性

這段代碼展示了Spring JDBC的基本用法,遵循了接口編程與依賴注入的設計思想。

修改3個檔案

package com.tzu2.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.http.HttpEntity;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

import com.tzu2.domain.Customers;
import com.tzu2.domain.CustomersRepo;
import com.tzu2.domain.Message;

@RestController
public class CustomerService {
	//Data Field注入JdbcTemplate
	@Autowired
	private JdbcTemplate jdbcTemplate;
	//注入自訂Repository
	@Autowired
	private CustomersRepo customersRepo;
	
	//傳遞一份Json進來  進行相對客戶更新作業
	//方法參數 採用參數注入Parameter Injection
	@PutMapping(path="/api/customers/update/rawdata",
			consumes="application/json",produces="application/json")
	public Message customersUpdate(@RequestBody Customers customers) {
		Message message=new Message();
		//更新客戶資料
		String sql="update customers set companyname=?,address=?,phone=?,email=?,country=? where customerid=?";
		try {
		int affect=jdbcTemplate.update(sql,
				//Lambda PreparedStatementSetter interface 
				(st)->{
					//注入PreparedStatement物件,設定參數內容
					st.setString(1, customers.getCompanyname());
					st.setString(2, customers.getAddress());
					st.setString(3, customers.getPhone());
					st.setString(4, customers.getEmail());
					st.setString(5, customers.getCountry());
					st.setString(6, customers.getCustomerid());
				}
				);
		if(affect>0) {
			message.setCode(200);
			message.setMsg("客戶資料更新成功");
		}else {
			message.setCode(200);
			message.setMsg("查無該客戶資料更新");
		}
		}catch(DataAccessException ex) {
			message.setCode(400);
			message.setMsg("客戶資料更新失敗");
		}
		
		return message;
	}

	
	//刪除相對客戶資料
	@DeleteMapping(path="/api/customers/delete/byid",produces="application/json")
	public void customersDelete(String customerid) {
		System.out.println(customersRepo.getTemplate());
		var result=customersRepo.delete(customerid);
		
		
	}
}

這段程式碼使用了Spring Boot实现了客户資料的更新和刪除功能,並通過RESTful API進行訪問,代碼分析如下:

  1. @RestController定義這個類是REST控制器

  2. @Autowired自動注入JdbcTemplate和CustomersRepo bean

  3. @PutMapping處理PUT請求,調用customersUpdate方法更新客戶資料

  4. customersUpdate使用JdbcTemplate執行更新語句

  5. Lamdba表達式實現了PreparedStatementSetter來設定参数

  6. @DeleteMapping處理DELETE請求,調用customersDelete方法

  7. customersDelete直接調用CustomersRepo的delete方法刪除資料

  8. CustomersRepo封裝了數據訪問邏輯,提高代碼重用性

  9. 統一的返回類Message表示請求結果

這實現了一套典型的Spring Boot Web應用數據訪問層架構,通過接口編程和依賴注入提高了代碼的彈性與可維護性。

package com.tzu2.domain;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;

@Repository
public class CustomersRepo implements DBRepository<Customers,String> {
	
	@Autowired
	private JdbcTemplate template;
	
	public CustomersRepo() {
		System.out.println("Customes Repository建構了...");
	}
	
//	//屬性注入 Property Injection(setXxx getXxxx xxx is Property
//	@Override
//	public void setJdbcTemplate(JdbcTemplate template) {
//		this.template=template;
//		
//	}

	@Override
	public boolean insert(Customers entity) throws DataAccessException {
		// TODO Auto-generated method stub
		return false;
	}

	@Override
	public Customers selectForObject(String key) throws DataAccessException {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public List<Customers> selectForList(String key) throws DataAccessException {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public boolean update(Customers entity) throws DataAccessException {
		// TODO Auto-generated method stub
		return false;
	}

	@Override
	public boolean delete(String key) throws DataAccessException {
		
		this.template.update("delete from customers where customerid=?",key);
		return true;
	}

	public JdbcTemplate getTemplate() {
		return template;
	}

	
	
}

這段代碼實現了一個CustomerRepository,用於對customer資料表進行CRUD操作,代碼解析如下:

  1. @Repository標註這是一个Spring資料訪問層元件

  2. 實現了泛型接口DBRepository,定義基礎的CRUD方法

  3. @Autowired自動注入JdbcTemplate bean

  4. 重寫了delete方法,調用JdbcTemplate執行刪除語句

  5. 其他CRUD方法暫時返回空結果或false

  6. 提供了getTemplate()方法返回JdbcTemplate

  7. 依賴注入取代了類中的set注入方式

總結:

  • 定義Repository接口來統一數據訪問

  • 通過JdbcTemplate實現CRUD操作

  • Repository和JdbcTemplate結合完成數據訪問職責

  • 依賴注入提高了代碼的解耦合性

package com.tzu2.myweb2;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;

@SpringBootApplication
@ComponentScan(basePackages = {"com.tzu2.controllers","com.tzu2.config","com.tzu2.service","com.tzu2.domain"})

public class Myweb2Application {

	public static void main(String[] args) {
		SpringApplication.run(Myweb2Application.class, args);
	}

}

這是一個標準的Spring Boot應用啟動類的代碼,主要功能是啟動Spring Boot應用。

代碼解說:

  1. @SpringBootApplication 標記這是Spring Boot應用

  2. @ComponentScan定義需要掃描的包以找出Spring組件,通常是控制器、服務、數據訪問對象等

  3. main方法調用SpringApplication.run啟動應用

  4. SpringApplication.run參數:

  • Myweb2Application.class: 這個Spring Boot應用的啟動類

  • args: 命令行參數

  1. 通過@ComponentScan自定義掃描的包,就能實現部分掃描而不是全局掃描

  2. 掃描到的包可以找到控制器、服務、數據訪問對象並自動創建Bean

這樣基於Spring Boot的應用就可以通過這個啟動類啟動了。

用POSTMAN測試http://localhost:8080/api/customers/delete/byid?customerid=C0001
https://ithelp.ithome.com.tw/upload/images/20231109/20119035lV7GDl8bIN.png

查看資料庫確實刪除了~
https://ithelp.ithome.com.tw/upload/images/20231109/20119035KYK8iviYGL.png

再新增另一個檔案,並用右鍵get跟set


package com.tzu.domain;

public class StatusMessage {
	
	private int code;
	private String msg;
	private String errorCode;
	public int getCode() {
		return code;
	}
	public void setCode(int code) {
		this.code = code;
	}
	public String getMsg() {
		return msg;
	}
	public void setMsg(String msg) {
		this.msg = msg;
	}
	public String getErrorCode() {
		return errorCode;
	}
	public void setErrorCode(String errorCode) {
		this.errorCode = errorCode;
	}

}

StatusMessage 類別是一個普通的 Java 類別,用於表示一個狀態訊息。這個類別具有以下屬性:

  • code: 代表狀態碼(通常是一個整數),用於指示操作的結果。
  • msg: 代表訊息,通常是一個描述操作結果的字串。
  • errorCode: 可能是一個錯誤代碼或錯誤訊息的額外詳細資訊。

這個類別允許創建一個物件,以便在應用程式中輕鬆地傳遞關於操作狀態的資訊,例如成功、失敗、錯誤原因等。

通常,在操作資料庫或執行 Web 服務時,使用 StatusMessage 物件來回應給客戶端,以告訴它們操作的結果。這是一個標準的做法,使應用程式能夠提供有意義的回應給使用者或其他系統。

加入com.tzu.domain

package com.tzu.myweb;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;

@SpringBootApplication
@ComponentScan(basePackages = {"com.tzu.controllers","com.tzu.config","com.tzu.service","com.tzu.domain"})

public class MywebApplication {

	public static void main(String[] args) {
		SpringApplication.run(MywebApplication.class, args);
	}

}

這個 Java 類別 MywebApplication 是 Spring Boot 應用程式的主入口點。它使用 @SpringBootApplication 標註來宣告這是一個 Spring Boot 應用程式,並進行自動配置。同時,也使用 @ComponentScan 來指示 Spring 應掃描特定的包以便找到所需的組件。

在這個類別中,你可以看到以下要點:

  • public static void main(String[] args): 這是 Java 應用程式的主入口點。當你執行該程式時,它啟動 Spring Boot 應用程式。

  • @SpringBootApplication: 這是一個元註解,它結合了多個註解,包括 @Configuration@EnableAutoConfiguration@ComponentScan。這樣可以讓 Spring Boot 自動配置,並啟用 Spring 的組件掃描,使它能夠找到所有的 Spring 組件。

  • @ComponentScan: 這是用來指示 Spring 應掃描特定包的註解。在這個情況下,它告訴 Spring 應掃描 com.tzu.controllerscom.tzu.configcom.tzu.servicecom.tzu.domain 這些包,以便找到組件類別。這樣做是為了確保 Spring 能夠找到應用程式的控制器、服務、設定和領域類別。

當你執行這個應用程式時,Spring Boot 將初始化並執行你的應用程式,並根據你的配置自動配置 Spring 環境。

整個類別要變元件,修改CustomersController的CODE

package com.tzu.controllers;
import java.util.List;

import javax.sql.DataSource;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import com.tzu.domain.Customer;
import com.tzu.domain.Customers;

@Controller
@RequestMapping(path="/customers")
public class CustomersController {
	//Attribute(Data Field) Field Injection
	@Autowired
	private JdbcTemplate jdbcTemplate; //DataSource會注入控制反轉
	
	//查詢功能設計
	@RequestMapping(path="/qry/country",
			method= {RequestMethod.GET,RequestMethod.POST})
	public String customersQry(String country,Model model) {
		//是否第一次請求 沒有傳遞國家別
		if(country==null) {
			return "customersqrycountry"; //View Page名稱
		}else {
			System.out.println("查詢國家別:"+country);
			DataSource ds=jdbcTemplate.getDataSource();
			System.out.println(ds.toString());		
			//1.採用DAO設計模式(Spring Boot data jdbc)
			String sql="SELECT ID,name,address,phone,country "
					+ "FROM sakila.customer_list where country=?";
			//2.進行國家別相關客戶查詢
			List<Customer> result=jdbcTemplate.query(sql,
					//傳遞一個程序當作參數(RowMapper/maprow 介面) 使用Lambda 
					//查詢符合每一筆 逐筆傳遞進來callback 自訂程序封裝結果
					(rs,num)->{
						//將相對紀錄封裝成自訂JavaBean-Customer
						Customer customer=new Customer();
						//注入相對記錄欄位於JavaBean物件中
						customer.setId(rs.getShort("ID"));
						customer.setName(rs.getString("name"));
						customer.setAddress(rs.getString("address"));
						customer.setPhone(rs.getString("phone"));
						customer.setCountry(rs.getString("country"));
						return customer;
					},
					country
					);
			System.out.println("查詢結果:"+result.size());
			//透過注入Model持續查詢結果物件到View Page
			model.addAttribute("result",result);
			model.addAttribute("country",country);
			//3.查詢結果狀態管理 調用View Page去進行查詢結果渲染(使用thymeleaf template engine)
			//採用postback 回來 要進行查詢
			return "customersqrycountry";
		}
	}

	//查詢客戶所有資料,將查詢結果產生(後端List<>) to 前端的JavaScript 陣列(封裝每一筆資料物件)
	//只要超連結方式點選到這裡 採用GET
	@GetMapping(path="/allcustomers")
	public String customesAll(Model model) {
		//1.借助注入進來的JdbcTemplate 進行客戶資料customers所有記錄查詢
		String sql="select customerid,companyname,address,phone,email,country from customers";
		List<Customers> result=
				jdbcTemplate.query(sql,
						BeanPropertyRowMapper.newInstance(Customers.class));
		System.out.println("記錄數:"+result.size());
		//2.查詢結果是一個List集合物件 如何轉換成JavaScript 陣列???
		model.addAttribute("data",result);
		//3調用頁面
		return "customersall";
	}
}

這個 CustomersController 是一個 Spring MVC 控制器,它處理有關客戶的相關請求。讓我們來看看這個控制器的主要要點:

  1. @Controller 標註:這個標註宣告這是一個 Spring MVC 控制器。

  2. @RequestMapping:它被用來定義該控制器處理的 URL 路徑。在這個情況下,CustomersController 處理的路徑是 /customers

  3. @Autowired:這標註被用來自動注入 JdbcTemplate 物件,它用於執行 SQL 查詢。

  4. customersQry 方法:這個方法處理客戶查詢功能。如果 country 參數為 null,則它返回 customersqrycountry 這個視圖頁面的名稱,以便用戶輸入國家別。如果 country 參數不為 null,則它執行相關的 SQL 查詢,將查詢結果封裝為 Customer 物件的列表,然後將結果傳遞到視圖頁面,這樣可以呈現給用戶。

  5. customersAll 方法:這個方法處理獲取所有客戶資料的請求。它執行 SQL 查詢以檢索所有客戶記錄,然後將結果封裝為 Customers 物件的列表。這個列表最後會被傳遞到視圖頁面。

這個控制器主要負責處理客戶相關的請求,並將查詢結果傳遞到視圖頁面,這樣可以呈現給用戶。

也修改CustomerService

package com.tzu.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.http.HttpEntity;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

import com.tzu.domain.Customers;
import com.tzu.domain.CustomersRepo;
import com.tzu.domain.Message;
import com.tzu.domain.StatusMessage;
@RestController
public class CustomerService {
	//Data Field注入JdbcTemplate
	@Autowired
	private JdbcTemplate jdbcTemplate;
	//注入自訂Repository
	@Autowired
	private CustomersRepo customersRepo;
	
	//傳遞一份Json進來  進行相對客戶更新作業
	//方法參數 採用參數注入Parameter Injection
	@PutMapping(path="/api/customers/update/rawdata",
			consumes="application/json",produces="application/json")
	public Message customersUpdate(@RequestBody Customers customers) {
		Message message=new Message();
		//更新客戶資料
		String sql="update customers set companyname=?,address=?,phone=?,email=?,country=? where customerid=?";
		try {
		int affect=jdbcTemplate.update(sql,
				//Lambda PreparedStatementSetter interface 
				(st)->{
					//注入PreparedStatement物件,設定參數內容
					st.setString(1, customers.getCompanyname());
					st.setString(2, customers.getAddress());
					st.setString(3, customers.getPhone());
					st.setString(4, customers.getEmail());
					st.setString(5, customers.getCountry());
					st.setString(6, customers.getCustomerid());
				}
				);
		if(affect>0) {
			message.setCode(200);
			message.setMsg("客戶資料更新成功");
		}else {
			message.setCode(200);
			message.setMsg("查無該客戶資料更新");
		}
		}catch(DataAccessException ex) {
			message.setCode(400);
			message.setMsg("客戶資料更新失敗");
		}
		
		return message;
	}

	
	//刪除相對客戶資料
	@DeleteMapping(path="/api/customers/delete/byid",produces="application/json")
	public HttpEntity customersDelete(String customerid) {
		System.out.println(customersRepo.getTemplate());
		var result=customersRepo.delete(customerid);
		HttpEntity<Object> httpEntity=null;
		//判斷
		if (result>0) {
			//刪除成功
			Message msg=new Message();
			msg.setCode(200);
			msg.setMsg("客戶資料刪除成功");
			httpEntity=new HttpEntity<Object>(msg);
			
		}else {
			//刪除不到資料
			StatusMessage msg=new StatusMessage();
			msg.setCode(400);
			msg.setMsg("沒有這一個客戶");
			msg.setErrorCode("notfound");
			httpEntity=new HttpEntity<Object>(msg);
		}
		return httpEntity;
		
	}
}

這是一個 Spring Boot 的 RESTful Web Service,它處理有關客戶資料的更新和刪除操作。以下是這個服務中的主要要點:

  1. @RestController 標註:這個標註表明這是一個 RESTful Web Service,用於處理 HTTP 請求。

  2. @Autowired 標註:這些標註被用來注入 JdbcTemplateCustomersRepo 物件,它們用於數據訪問和操作客戶資料。

  3. @PutMapping 方法:這個方法處理客戶資料的更新操作。它接受來自客戶端的 JSON 數據,並將其解析為 Customers 物件。然後,它執行 SQL 更新語句以更新客戶資料。如果更新成功,將返回一個帶有成功消息的 Message 物件,否則,將返回包含錯誤消息的 Message 物件。

  4. @DeleteMapping 方法:這個方法處理客戶資料的刪除操作。它接受客戶端發送的客戶編號 customerid,然後調用 customersRepodelete 方法來執行刪除操作。根據操作結果,它將返回不同的 HttpEntity 物件。如果刪除成功,將返回一個帶有成功消息的 Message 物件;如果找不到相關客戶資料,將返回帶有錯誤消息的 StatusMessage 物件。

這個服務提供了更新和刪除客戶資料的 API 端點,允許客戶端進行相應的操作。

再修改CustomersRepo

package com.tzu.domain;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;

@Repository
public class CustomersRepo implements DBRepository<Customers,String> {
	//Attribute as Data Field
	@Autowired
	private JdbcTemplate template;
	
	public CustomersRepo() {
		System.out.println("Customes Repository建構了...");
	}
	
//	//屬性注入 Property Injection(setXxx getXxxx xxx is Property
//	@Override
//	public void setJdbcTemplate(JdbcTemplate template) {
//		this.template=template;
//		
//	}

	@Override
	public boolean insert(Customers entity) throws DataAccessException {
		// TODO Auto-generated method stub
		return false;
	}

	@Override
	public Customers selectForObject(String key) throws DataAccessException {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public List<Customers> selectForList(String key) throws DataAccessException {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public boolean update(Customers entity) throws DataAccessException {
		// TODO Auto-generated method stub
		return false;
	}

	@Override
	public int delete(String key) throws DataAccessException {
		System.out.println(template);
		//刪除作業
		int affect=this.template.update("delete from customers where customerid=?",key);
		return affect;
	}

	public JdbcTemplate getTemplate() {
		return template;
	}
	

}

這是一個 Spring Boot 的 Repository 類別,用於對客戶資料進行操作。以下是這個類別的主要要點:

  1. @Repository 標註:這個標註表明這是一個 Spring Framework 的 Repository,用於數據存取操作。

  2. @Autowired 標註:這個標註用於注入 JdbcTemplate 物件,用於執行 SQL 查詢和更新操作。

  3. public CustomersRepo() 建構函數:這個建構函數只是一個示例,用於在建立 CustomersRepo 物件時輸出一條日誌消息。它不執行實際的操作。

  4. DBRepository 接口的實現:這個類別實現了 DBRepository 介面,並提供了相關的操作方法。在這裡,你實現了 delete 方法,該方法接受客戶編號 key 並執行 SQL 刪除操作。它返回影響的行數作為整數。

  5. public JdbcTemplate getTemplate() 方法:這個方法允許外部訪問 JdbcTemplate 物件。這在你的控制器類別 CustomerService 中用於執行客戶刪除操作。

這個 Repository 類別是 Spring Boot 應用程序中與客戶資料數據庫交互的一個重要組件,它實現了 DBRepository 接口,允許進行客戶資料的刪除操作。

Message檔Code

package com.tzu.domain;

//服務處理之後回應訊息內容(Json)
public class Message implements java.io.Serializable {
	private int code;
	private String msg;
	public int getCode() {
		return code;
	}
	public void setCode(int code) {
		this.code = code;
	}
	public String getMsg() {
		return msg;
	}
	public void setMsg(String msg) {
		this.msg = msg;
	}
	

}

這是一個用於定義服務處理之後回應訊息內容的 Java 類別。以下是這個類別的主要要點:

  1. int code 屬性:用於表示回應訊息的狀態碼。通常,HTTP 狀態碼(例如 200、400、404 等)或自定義的應用程式狀態碼會存儲在這個屬性中。這個狀態碼通常用於指示處理請求的結果。

  2. String msg 屬性:用於存儲回應訊息的文字描述。這可以是關於處理結果的詳細資訊,通常是一個說明性的文字消息。

  3. 對應的 getter 和 setter 方法:這些方法允許存取 codemsg 屬性的值。

這個 Java 類別的目的是定義回應訊息的格式,通常用於將服務處理結果以 JSON 格式返回給客戶端。 code 可以用於表示處理狀態,而 msg 可以包含詳細的描述信息,以便客戶端了解處理結果。

DBRepository檔CODE

package com.tzu.domain;

import java.util.List;

import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.JdbcTemplate;
//進行泛型設定Generic
public interface DBRepository<T,K> {
	//屬性setXxxx()注入規範 注入 Jdbctemplate
	//public void setJdbcTemplate(JdbcTemplate template);
	//CRUD方法
	public boolean insert(T entity) throws  DataAccessException;
	//查詢 單筆查詢 配合Key
	public T selectForObject(K key) throws  DataAccessException;
	//多筆查詢
	public List<T> selectForList(K key) throws  DataAccessException;
	//修改
	public boolean update(T entity) throws  DataAccessException;
	public int delete(K key) throws  DataAccessException;

}

更新了DBRepository 接口,並添加了 delete 方法,並將返回類型由 boolean 改為 int,這是一個好的改進。這意味著 delete 方法現在會返回已刪除的記錄數。

這個更改使 delete 方法更具信息性,因為它可以告訴您實際刪除了多少條記錄。通常,當刪除成功時,它會返回受影響的行數(通常是 1),如果沒有匹配的記錄被刪除,它可能返回 0。

此更改將允許您在處理刪除操作後更容易確定實際刪除了多少條記錄,這是一個很有用的信息,特別是在某些情況下需要知道刪除操作的效果。

用POSTMAN測試http://localhost:8080/api/customers/delete/byid?customerid=C0001
https://ithelp.ithome.com.tw/upload/images/20231109/201190351JUUbNWmPr.png

剛剛測試過已被刪除~

改成http://localhost:8080/api/customers/delete/byid?customerid=C0002
https://ithelp.ithome.com.tw/upload/images/20231109/20119035wKIAYCoPJg.png

再回到資料庫看

https://ithelp.ithome.com.tw/upload/images/20231109/201190351WpkOZ7kPs.png

確實刪除

謝謝大家收看
/images/emoticon/emoticon41.gif


上一篇
Springboot~跳出對話框
下一篇
Springboot~練習與外部資料連結
系列文
自己開發一個~?30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言